#!/usr/bin/env bash
# shellcheck disable=SC2128

# Shows the usage
function usage() {
  cat << EOS
Usage:
  git standup [-a <author name>] [-w <weekstart-weekend>] [-d <since-days-ago>] [-u <until-days-ago>] [-m <max-dir-depth>] [-g] [-h] [-f] [-c]

  -a      - Specify author to restrict search to
  -b      - Specify branch to restrict search to (unset: all branches, "\$remote/\$branch" to include fetches)
  -w      - Specify weekday range to limit search to
  -m      - Specify the depth of recursive directory search
  -F      - Force recursion up to speficied depth
  -L      - Toggle inclusion of symbolic links in recursive directory search
  -d      - Specify the number of days back to include
  -u      - Specify the number of days back until this day
  -D      - Specify the date format for "git log" (default: relative, other: local|default|iso|rfc|short|raw)
  -h      - Display this help screen
  -g      - Show if commit is GPG signed (G) or not (N)
  -f      - Fetch the latest commits beforehand
  -s      - Silences the no activity message (useful when running in a directory having many repositories)
  -r      - Generate a file with the report
  -c      - Show diffstat for every matched commit
  -A      - List commits after this date
  -B      - List commits before this date
  -R      - Display the author date instead of the committer date

Examples:
  git standup -a "John Doe" -w "MON-FRI" -m 3
EOS
}

# Sets up the colored output
function colored() {
  RED=$(tput setaf 1)
  GREEN=$(tput setaf 2)
  YELLOW=$(tput setaf 3)
  BLUE=$(tput setaf 4)
  BOLD=$(tput bold)
  UNDERLINE=$(tput smul)
  NORMAL=$(tput sgr0)

  GIT_PRETTY_DATE="%cd"
  if [[ ${option_R:=} ]]; then
    GIT_PRETTY_DATE="%ad"
  fi

  GIT_PRETTY_FORMAT="%Cred%h%Creset - %s %Cgreen($GIT_PRETTY_DATE) %C(bold blue)<%an>%Creset"
  COLOR=always

  if [[ ${option_g:=} ]]; then
    GIT_PRETTY_FORMAT="$GIT_PRETTY_FORMAT %C(yellow)gpg: %G?%Creset"
  fi
}

# Sets up the uncolored output
function uncolored() {
  # shellcheck disable=SC2034
  RED=""
  GREEN=""
  YELLOW=""
  # shellcheck disable=SC2034
  BLUE=""
  BOLD=""
  UNDERLINE=""
  NORMAL=""

  GIT_PRETTY_DATE="%cd"
  if [[ $option_R ]]; then
    GIT_PRETTY_DATE="%ad"
  fi

  GIT_PRETTY_FORMAT="%h - %s ($GIT_PRETTY_DATE) <%an>"
  COLOR=never

  if [[ $option_g ]]; then
    GIT_PRETTY_FORMAT="$GIT_PRETTY_FORMAT gpg: %G?\n"
  else
    GIT_PRETTY_FORMAT="$GIT_PRETTY_FORMAT \n"
  fi
}

function writeFile() {
  echo -e "$1" >> "${REPORT_FILE_PATH}"
}

function runStandup() {
  # Fetch the latest commits, if required
  if [[ ${option_f:=} ]]; then
    echo "${BOLD}${GREEN}Fetching commits in ${YELLOW}${UNDERLINE}${BOLD}${BASENAME}${NORMAL}"
    git fetch --all > /dev/null 2>&1
  fi

  {
    # shellcheck disable=SC2086
    GITOUT=$(eval ${GIT_LOG_COMMAND} 2> /dev/null)
  } || {
    GITOUT=""
  }

  # If `r` option was given then no output, just write the report
  if [[ -n ${option_r:=} ]]; then
    echo "Generating report for: ${CUR_DIR}"

    if [[ -n "$GITOUT" ]]; then
      writeFile "${CUR_DIR}\n $GITOUT"
    elif [[ -z ${option_s:=} ]]; then
      writeFile "${CUR_DIR}\n No activity found!\n"
    fi
  else
    ## Only output if there is some activity
    if [[ -n "$GITOUT" ]]; then
      echo "${BOLD}${UNDERLINE}${YELLOW}$CUR_DIR${NORMAL}"
      echo "$GITOUT"
    elif [[ -z $option_s ]]; then ## Show the no activity message only if the `s` flag is not there
      echo "${BOLD}${UNDERLINE}${YELLOW}$CUR_DIR${NORMAL}"
      if [[ ${AUTHOR} = '.*' ]]; then
        echo "${YELLOW}No commits found during this period.${NORMAL}"
      else
        echo "${YELLOW}No commits from $AUTHOR during this period.${NORMAL}"
      fi
    fi
  fi
}

while getopts "hgfsd:u:a:w:m:D:A:B:LrcRFb:" opt; do
  case $opt in
    h | d | u | a | w | m | g | D | f | s | L | r | A | B | c | R | F | b)
      declare "option_$opt=${OPTARG:-0}"
      ;;
    \?)
      echo >&2 "Use 'git standup -h' to see usage info"
      exit 1
      ;;
  esac
done

shift $((OPTIND - 1))

if [[ $# -gt 0 ]]; then
  echo >&2 "Invalid arguments: $*"
  echo >&2 "Use 'git standup -h' to see usage info"
  exit 1
fi

# Main script
if [[ ${option_h:=} ]]; then
  usage
  exit 0
fi

# Use colors, but only if connected to a terminal, and that terminal supports them.
if [[ -t 1 ]] && [[ -n "$TERM" ]] && command -v tput &> /dev/null && tput colors &> /dev/null; then
  ncolors=$(tput colors)
  if [[ -n "$ncolors" ]] && [[ "$ncolors" -ge 8 ]] && [[ -z "$option_r" ]]; then
    colored
  else
    uncolored
  fi
else
  uncolored
fi

## Set the necessary variables for standup
SINCE="yesterday"
MAXDEPTH=2
INCLUDE_LINKS=
RAN_FROM_DIR=$(pwd)
REPORT_FILE_PATH="${RAN_FROM_DIR}/git-standup-report.txt"
STAT=

# If report is to be generated, remove the existing report file if any
if [[ -n $option_r ]]; then
  rm -rf "${REPORT_FILE_PATH}"
fi

if [[ ${option_m:=} ]]; then
  MAXDEPTH="$((${option_m:=} + 1))"
fi

if [[ ${option_L:=} ]]; then
  INCLUDE_LINKS="-L"
fi

if [[ ${option_c:=} ]]; then
  STAT="--stat"
fi

## If -d flag is there, use its value for the since
if [[ ${option_d:=} ]] && [[ $option_d -ne 0 ]]; then
  SINCE="$option_d days ago"
else
  ## -d flag is not there, go on with the normal processing
  WEEKSTART="$(cut -d '-' -f 1 <<< "${option_:=w}")"
  WEEKSTART=${WEEKSTART:="Mon"}

  WEEKEND="$(cut -d '-' -f 2 <<< "${option_w:=}")"
  WEEKEND=${WEEKEND:="Fri"}

  ## In case it is the start of week, we need to
  ## show the commits since the last weekend
  shopt -s nocasematch
  if [[ ${WEEKSTART} == "$(date +%a)" ]]; then
    SINCE="last $WEEKEND"
  fi
fi

## If -u flag is there, use its value for the until
if [[ ${option_u:=} ]] && [[ $option_u -ne 0 ]]; then
  UNTIL_OPT="--until=\"$option_u days ago\""
elif [[ ${option_B:=} ]]; then
  UNTIL_OPT="--until=\"$option_B\""
fi

if [[ ${option_A:=} ]]; then
  AFTER_OPT="--after=\"$option_A\""
fi

GIT_DATE_FORMAT=${option_D:-relative}

# For when the command has been run in a non-repo directory
if [[ ${option_F:=} || ! -d ".git" || -f ".git" ]]; then
  BASE_DIR=$(pwd)
  # Set delimiter to newline for the loop
  IFS=$'\n'

  if [[ -f ".git-standup-whitelist" ]]; then
    SEARCH_PATH=$(cat .git-standup-whitelist)
  else
    SEARCH_PATH=.
  fi

  # Recursively search for git repositories
  PROJECT_DIRS=$(find ${INCLUDE_LINKS} ${SEARCH_PATH} -maxdepth ${MAXDEPTH} -mindepth 0 -name .git)
elif [[ -f ".git" || -d ".git" ]]; then
  PROJECT_DIRS=("$(pwd)/.git")
fi

# if project directories is still empty
# we might be sitting inside a git repo
if [[ -z ${PROJECT_DIRS} ]]; then
  ROOT_DIR_COMMAND="git rev-parse --show-toplevel"
  PROJECT_ROOT=$(eval "${ROOT_DIR_COMMAND}" 2> /dev/null)

  if [[ -z ${PROJECT_ROOT} ]]; then
    echo "${YELLOW}You must be inside a git repository!${NORMAL}"
    exit 0
  fi

  PROJECT_DIRS=("${PROJECT_ROOT}/.git")
fi

# Foreach of the project directories, run the standup
IFS=$'\n'
for DIR in ${PROJECT_DIRS}; do
  PROJECT_DIR=$(dirname "$DIR")
  cd "$PROJECT_DIR" || exit
  CUR_DIR=$(pwd)
  BASENAME=$(basename "$CUR_DIR")

  # continue if not a git directory
  if [[ ! -d ".git" || -f ".git" ]]; then
    cd "${BASE_DIR}" || exit
    continue
  fi

  AUTHOR=$(git config user.name)

  if [[ ${option_a:=} ]]; then
    # In case the parameter
    if [[ $option_a = 'all' ]]; then
      AUTHOR=".*"
    else
      AUTHOR="$option_a"
    fi
  fi

  GIT_BRANCH_FILTER="--all"
  if [[ ${option_b:=} ]]; then
    GIT_BRANCH_FILTER="--first-parent $option_b"
  fi

  GIT_LOG_COMMAND="git --no-pager log \
    ${GIT_BRANCH_FILTER}
    --no-merges
    --since=\"$SINCE\"
    ${UNTIL_OPT}
    ${AFTER_OPT}
    --author=\"$AUTHOR\"
    --abbrev-commit
    --oneline
    --pretty=format:'$GIT_PRETTY_FORMAT'
    --date='$GIT_DATE_FORMAT'
    --color=$COLOR
    ${STAT}"

  runStandup

  cd "${BASE_DIR}" || exit
done
unset IFS
